home
***
CD-ROM
|
disk
|
FTP
|
other
***
search
/
Language/OS - Multiplatform Resource Library
/
LANGUAGE OS.iso
/
a_utils
/
expanded.lha
/
expanded
/
src
/
Map.C
< prev
next >
Wrap
C/C++ Source or Header
|
1992-03-19
|
51KB
|
1,794 lines
//
// Linear-Affine-Projective Geometry Package
//
// Map.C
//
// $Header$
//
// William J.R. Longabaugh
// University of Washington
//
// Implementation of the linear-affine-projective geometry
// package described in William J.R. Longabaugh, "An Expanded
// System for Coordinate-Free Geometric Programming", Master's
// thesis, University of Washington, 1992.
//
// Copyright (c) 1992, William J.R. Longabaugh
// Copying, use and development for non-commercial purposes permitted.
// All rights for commercial use reserved.
// This software is unsupported and without warranty; it is
// provided "as is".
//
// ***********************************************************************
#include <math.h>
#include "Lap.h"
// ***********************************************************************
// ***********************************************************************
//
// Map Class
//
// ***********************************************************************
// ***********************************************************************
//
// Private/protected member functions
//
// ***********************************************************************
//
// This routine is used by map constructors to convert lists
// of arbitrary geometric objects into objects of the desired type
// in the desired space. A matrix containing std. coords of the
// objects is built:
//
Boolean Map::ConvertList(GeObList& v, GeObType targ,
SubSet& dest, Matrix* temp)
{
Space destspace = dest.EmbeddingSpace();
// Determine if the matrix reps. need to be normalized:
Boolean norm =
((destspace.Holds() == PROJ_SPACE) && (dest.Holds() == AFFINE_SUBSET));
for (int i = 0; i < v.Length(); i++) {
GeOb tempgeob = v[i].MapTo(targ);
if (tempgeob.SpaceOf() != destspace) {
return (FALSE);
}
if (!dest.IsIn(tempgeob)) {
return (FALSE);
}
if (norm) {
tempgeob = dest.Normalize(tempgeob);
}
(*temp)[i] = tempgeob.MatrixRep()[0];
}
return (TRUE);
}
// ***********************************************************************
//
// Apply associated linear map (without explicitly building it):
//
GeOb Map::ApplyAL(GeOb& v)
{
static char* sig = "GeOb Map::ApplyAL(void)";
GeOb temp = v.MapTo(AFF_VECTOR);
if (temp.SpaceOf() != domain.TangentSub().EmbeddingSpace()) {
errh.ErrorExit(sig, "Object cannot be mapped to domain space", *this, v);
}
if (!domain.TangentSub().IsIn(temp)) {
errh.ErrorExit(sig, "Mapped object not in specified domain subset",
*this, temp);
}
// The range depends on the character of the affine map. If it is
// to an affine subset of an affine space, the range is the
// corresponding subspace of the tangent space. If it is to an
// affine subset of a vector space, the range is a parallel vector
// subspace of the vector space. If it is to a vector subset,
// this range is the same vector subset.
if (range.EmbeddingSpace().Holds() == AFF_SPACE) {
Matrix alt = temp.MatrixRep() *
domain.EmbeddingSpace().HoistTangent() * t;
Matrix holdmatr = range.EmbeddingSpace().HoistTangent();
GeOb retval(AFF_VECTOR, range.TangentSub().EmbeddingSpace(),
alt * Transpose(holdmatr));
return retval;
} else {
Matrix alt = temp.MatrixRep() *
domain.EmbeddingSpace().HoistTangent() * t;
GeOb retval(VECTOR, range.EmbeddingSpace(), alt);
return retval;
}
}
// ***********************************************************************
//
// Given all the information, builds a new Map
//
Map::Map(MapType ty, Boolean i, SubSet& r,
SubSet& d, Matrix& tm, Matrix& it, Boolean isdf,
Boolean hal, GeObType dt, GeObType rt) : range(r), domain(d),
t(tm), invt(it)
{
invertible = i;
type = ANY_MAP;
holds = ty;
hasAL = hal;
isDefault = isdf;
domtype = dt;
rettype = rt;
}
// ***********************************************************************
//
// Dumps out all the data for a map:
//
void Map::data_out(ostream& c, int indent, char* name)
{
char *ibloc = new char[indent + 1];
for (int i = 0; i < indent; i++) {
*(ibloc + i) = ' ';
}
*(ibloc + indent) = '\0';
c << ibloc << ast;
c << ibloc << name;
c << ibloc << "Type of map: ";
MapTypeOut(c, type);
c << "\n";
c << ibloc << "Currently holds: ";
MapTypeOut(c, holds);
c << "\n";
if (holds != NULL_MAP) {
c << ibloc << "Domain subset:\n";
domain.debug_out(c, indent + ERR_IND);
c << ibloc << "Range subset:\n";
range.debug_out(c, indent + ERR_IND);
c << ibloc << "Matrix representation of map:\n";
t.debug_out(c, indent + ERR_IND);
c << ibloc << "Type of domain argument: ";
GeObTypeOut(c, domtype);
c << "\n" << ibloc << "Type of result: ";
GeObTypeOut(c, rettype);
if (invertible) {
c << "\n" << ibloc << "Matrix representation of inverse:\n";
invt.debug_out(c, indent + ERR_IND);
} else {
c << "\n" << ibloc << "Map is not invertible\n";
}
if (hasAL) {
c << ibloc << "Map has an associated linear map\n";
} else {
c << ibloc << "Map does not have an associated linear map\n";
}
if (isDefault) {
c << ibloc << "Map is a default map\n";
} else {
c << ibloc << "Map is not a default map\n";
}
}
c << ibloc << ast;
delete ibloc;
return;
}
// ***********************************************************************
//
// Public member functions
//
// ***********************************************************************
//
// Need to do memberwise initialization, since matrix class members need
// to do memberwise initialization:
//
Map::Map(Map &m) : range(m.range), domain(m.domain), t(m.t), invt(m.invt)
{
invertible = m.invertible;
type = ANY_MAP;
holds = m.holds;
hasAL = m.hasAL;
isDefault = m.isDefault;
domtype = m.domtype;
rettype = m.rettype;
}
// ***********************************************************************
//
// Need to do memberwise assignment, since Matrix class members need to
// do memberwise assignment:
//
Map& Map::operator=(Map &m)
{
//
// This weird checking is required to bypass the apparent inheritance of
// assignment under g++ 1.37:
//
if ((type != ANY_MAP) && (type != m.holds) && (m.holds != NULL_MAP)) {
errh.ErrorExit("Map& Map::operator=(Map&)",
"Illegal assignment attempted", *this, m);
}
range = m.range;
domain = m.domain;
t = m.t;
invt = m.invt;
invertible = m.invertible;
holds = m.holds;
hasAL = m.hasAL;
isDefault = m.isDefault;
domtype = m.domtype;
rettype = m.rettype;
return (*this);
}
// ***********************************************************************
//
// Output for debugging:
//
void Map::debug_out(ostream &c, int indent)
{
static char* name = "Map Object\n";
this->data_out(c, indent, name);
return;
}
// ***********************************************************************
//
// If the MultiMap has an atomic VSpace or ASpace for a domain, it
// can be converted to a map. Alternately, if it is a fully evaluated
// map that evaluates to a vector, it can be converted to a map from
// the dual space to the Reals.
//
Map::Map(MultiMap &m)
{
static char* sig = "Map::Map(MultiMap&)";
type = ANY_MAP;
if (m.FullyEvaluated()) {
*this = Map(GeOb(m));
} else {
// Check that the domain is an atomic VSpace or ASpace:
if (m.DomainSpace().CPSpaceSize() != 1) {
errh.ErrorExit(sig, "Domain of multimap must be an atomic space", m);
}
holds = (m.Holds() == MULTI_LINEAR) ? LIN_MAP : AFF_MAP;
range = m.RangeSpace().FullSet();
domain = m.DomainSpace().FullSet();
domtype = m.DomainType();
rettype = m.RetType();
isDefault = FALSE;
hasAL = (domtype == AFF_POINT);
int ddim = m.DomainSpace().MatrixSize();
int rdim = m.RangeSpace().MatrixSize();
t = Matrix(ddim, rdim);
for (int i = 0; i < ddim; i++) {
t[i] = (m.GetStdImage(i))[0];
}
if ((ddim == rdim) && (fabs(Det(t)) > EPSILON)) {
invertible = TRUE;
invt = Inverse(t);
} else {
invertible = FALSE;
}
}
}
// ***********************************************************************
//
// If the GeOb is a vector, it can be converted to a map from the
// dual space to the Reals.
//
Map::Map(GeOb& g)
{
static char* sig = "Map::Map(GeOb&)";
GeOb temp;
type = ANY_MAP;
if ((g.Holds() == VECTOR) || (g.Holds() == AFF_VECTOR)) {
temp = g;
} else if (g.CanMapTo(VECTOR)) {
temp = g.MapTo(VECTOR);
} else {
errh.ErrorExit(sig, "GeOb cannot be mapped to a vector", g);
}
range = Reals.FullSet();
domain = temp.SpaceOf().Dual().FullSet();
t = Transpose(temp.MatrixRep());
if (domain.EmbeddingSpace().Dim() == 1) {
invt = Inverse(t);
invertible = TRUE;
} else {
invertible = FALSE;
}
holds = LIN_MAP;
hasAL = FALSE;
isDefault = FALSE;
domtype = domain.EmbeddingSpace().NativeType();
rettype = VECTOR;
}
// ***********************************************************************
//
// Map application.
//
// It probably makes sense to also provide a special map application operator
// (to the implementing classes ONLY) that assumes its argument is
// the correct type and fills in a result at a location specified
// by an argument pointer. This would help standard mappings be more
// efficient.
//
GeOb Map::operator()(GeOb& v)
{
static char* sig = "GeOb Map::operator()(GeOb&)";
if (v.CanMapTo(domtype)) {
GeOb temp = v.MapTo(domtype);
if (temp.SpaceOf() != domain.EmbeddingSpace()) {
errh.ErrorExit(sig, "Object cannot be mapped to domain space", *this, v);
}
if (!domain.IsIn(temp)) {
errh.ErrorExit(sig, "Mapped object not in specified domain subset",
*this, temp);
}
if (domtype == PROJ_POINT && holds == AFF_MAP) {
temp = domain.Normalize(temp);
}
Matrix retmat = temp.MatrixRep();
if (!isDefault) {
retmat = retmat * t;
}
GeOb retval(rettype, range.EmbeddingSpace(), retmat);
return (retval);
} else if (hasAL && v.CanMapTo(AFF_VECTOR)) {
return (this->ApplyAL(v));
} else {
errh.ErrorExit(sig, "Object cannot be mapped into domain space",
*this, v);
}
}
// ***********************************************************************
//
// Since inverses are computed when the map is created, this is cheap.
//
Map Map::Inv(void)
{
static char* sig = "Map Map::Inv(void)";
if (!invertible) {
errh.ErrorExit(sig, "Map is not invertible", *this);
}
Boolean hal = ((holds == AFF_MAP) &&
(domtype != PROJ_POINT) &&
(rettype == AFF_POINT));
Map retval(holds, invertible, domain, range, invt, t,
isDefault, hal, rettype, domtype);
return (retval);
}
// ***********************************************************************
//
// Composition of two maps of the same type:
//
Map Map::Compose(Map &m)
{
static char* sig = "Map Map::Compose(Map&)";
Scalar factor = 1.0;
if (holds == m.Holds()) {
if (!domain.IsSubset(m.Range())) {
errh.ErrorExit(sig,
"Range of first map not a subset of domain of second map",
*this, m);
}
// Affine maps into/outof projective spaces need a factor to account
// for the need to normalize the offsets of the subsets:
if ((domain.EmbeddingSpace().Holds() == PROJ_SPACE) &&
(domain.Holds() == AFFINE_SUBSET)) {
factor = domain.NormVal(m.Range());
}
Boolean cinv = invertible && m.Invertible() && m.Range().IsSubset(domain);
Matrix ct = factor * m.t * t;
Matrix cinvt;
if (cinv) {
cinvt = (1.0 / factor) * invt * m.invt;
}
GeObType cdt = m.DomainType();
GeObType crt = rettype;
Boolean chal = ((holds == AFF_MAP) &&
(cdt == AFF_POINT) && (crt != PROJ_POINT));
Boolean cisdf = isDefault && m.isDefault;
Map retval(holds, cinv, range, m.Domain(), ct, cinvt,
cisdf, chal, cdt, crt);
return (retval);
} else {
errh.ErrorExit(sig, "Cannot compose different map types", *this, m);
}
}
// ***********************************************************************
//
// A common operation in graphics programming is to compose affine
// and projective maps to get a projective map. This operation
// does this composition by automatically casting any affine maps
// to projective maps:
//
ProjectiveMap Map::ComposeProj(Map &m)
{
ProjectiveMap first;
ProjectiveMap second;
if (holds == PROJ_MAP) {
first = *this;
} else {
first = this->InducedProjective();
}
if (m.Holds() == PROJ_MAP) {
second = m;
} else {
second = m.InducedProjective();
}
return (first.Compose(second));
}
// ***********************************************************************
//
// Given an affine map between two full affine spaces, we frequently desire
// the corresponding projective map between the neighboring projective
// completion spaces:
//
ProjectiveMap Map::InducedProjective(void)
{
static char* sig = "ProjectiveMap Map::InducedProjective(void)";
// For the current implementation, restrict this operation to affine
// maps between full affine subsets of affine spaces.
if (holds != AFF_MAP) {
errh.ErrorExit(sig,
"Can only take induced projective map for an affine map", *this);
}
if ((range.EmbeddingSpace().Holds() != AFF_SPACE ) ||
(domain.EmbeddingSpace().Holds() != AFF_SPACE ) ||
(!range.IsFullSpace()) || (!domain.IsFullSpace())) {
errh.ErrorExit(sig,
"Only implemented for maps between full affine spaces",
*this);
}
// Require that the map be invertible; otherwise, we would need to
// build a special domain subset for the projective map:
if (!invertible) {
errh.ErrorExit(sig, "Map must be invertible", *this);
}
SubSet td = domain.EmbeddingSpace().GetSpace(PROJECT_COMP).FullSet();
SubSet tr = range.EmbeddingSpace().GetSpace(PROJECT_COMP).FullSet();
GeObType trt = PROJ_POINT;
GeObType tdt = PROJ_POINT;
Matrix newt = td.EmbeddingSpace().AffineMapTo(AFFINE).t * t *
range.EmbeddingSpace().AffineMapTo(PROJECT_COMP).t;
Matrix newinvt = tr.EmbeddingSpace().AffineMapTo(AFFINE).t * invt *
domain.EmbeddingSpace().AffineMapTo(PROJECT_COMP).t;
Map retval(PROJ_MAP, invertible, tr, td, newt, newinvt,
FALSE, FALSE, tdt, trt);
return retval;
}
// ***********************************************************************
//
// Given an affine map between two full affine spaces, this gives the
// corresponding linear map between the neighboring linearization spaces:
//
LinearMap Map::InducedLinear(void)
{
static char* sig = "LinearMap Map::InducedLinear(void)";
// For the current implementation, restrict this operation to affine
// maps between full affine subsets of affine spaces.
if (holds != AFF_MAP) {
errh.ErrorExit(sig,
"Can only take induced linear map for an affine map", *this);
}
if ((range.EmbeddingSpace().Holds() != AFF_SPACE ) ||
(domain.EmbeddingSpace().Holds() != AFF_SPACE ) ||
(!range.IsFullSpace()) || (!domain.IsFullSpace())) {
errh.ErrorExit(sig,
"Only implemented for maps between full affine spaces",
*this);
}
SubSet td = domain.EmbeddingSpace().GetSpace(LINEARIZATION).FullSet();
SubSet tr = range.EmbeddingSpace().GetSpace(LINEARIZATION).FullSet();
GeObType trt = VECTOR;
GeObType tdt = VECTOR;
Matrix newt = td.EmbeddingSpace().AffineMapTo(AFFINE).t * t *
range.EmbeddingSpace().AffineMapTo(LINEARIZATION).t;
Matrix newinvt;
if (invertible) {
newinvt = tr.EmbeddingSpace().AffineMapTo(AFFINE).t * invt *
domain.EmbeddingSpace().AffineMapTo(LINEARIZATION).t;
}
Map retval(LIN_MAP, invertible, tr, td, newt, newinvt,
FALSE, FALSE, tdt, trt);
return retval;
}
// ***********************************************************************
//
// The transpose of a map V -> U is a map U' -> V'. Currently only
// implemented for maps between full spaces.
//
LinearMap Map::Trans(void)
{
static char* sig = "LinearMap Map::Trans(void)";
// Make sure this map is linear:
if (holds != LIN_MAP) {
errh.ErrorExit(sig, "Can only take transpose of a linear map", *this);
}
if (!range.IsFullSpace() || !domain.IsFullSpace()) {
errh.ErrorExit(sig,
"Only implemented for maps between full spaces", *this);
}
// Need to get the dual subspaces for the range and domain,
// and swap them:
SubSet td = range.EmbeddingSpace().Dual().FullSet();
SubSet tr = domain.EmbeddingSpace().Dual().FullSet();
GeObType trt = tr.EmbeddingSpace().NativeType();
GeObType tdt = td.EmbeddingSpace().NativeType();
Map retval(LIN_MAP, invertible, tr, td, Transpose(t),
Transpose(invt), FALSE, FALSE, tdt, trt);
return retval;
}
// ***********************************************************************
//
// If this is map A -> X, with A a subset of an affine space and X a
// subset of a linear or affine space, this is the map induced on the
// tangent space A.v:
//
LinearMap Map::AssocLinear(void)
{
static char* sig = "LinearMap Map::AssocLinear(void)";
// First need to make sure that the map has an associated linear map:
if (!hasAL) {
errh.ErrorExit(sig, "Map does not have an associated linear map", *this);
}
// Start to build matrix:
Matrix hold = domain.EmbeddingSpace().HoistTangent();
Matrix holdr = range.EmbeddingSpace().HoistTangent();
Matrix alt = hold * t;
Matrix alinvt;
if (invertible) alinvt = invt * Transpose(hold);
// The range depends on the character of the affine map. If it is
// to an affine subset of an affine space, the range is the
// corresponding subspace of the tangent space. If it is to an
// affine subset of a vector space, the range is a vector
// subspace in the vector space. If it is to a vector subset,
// this range is the same vector subset.
SubSet alr;
GeObType alrt;
if (range.EmbeddingSpace().Holds() == AFF_SPACE) {
alr = range.TangentSub();
alrt = AFF_VECTOR;
alt = alt * Transpose(holdr);
if (invertible) alinvt = holdr * alinvt;
} else if (range.Holds() == AFFINE_SUBSET) {
alr = range.TangentSub();
alrt = VECTOR;
} else {
alr = range;
alrt = VECTOR;
}
Map retval(LIN_MAP, invertible, alr, domain.TangentSub(), alt,
alinvt, FALSE, FALSE, AFF_VECTOR, alrt);
return retval;
}
// ***********************************************************************
//
// If this map is an affine functional (i.e. an affine map into the reals),
// this returns the vector in the dual of the tangent space corresponding
// to the associated linear map.
//
Vector Map::AssocDualVector(void)
{
static char* sig = "Vector Map::AssocDualVector(void)";
Space res = range.EmbeddingSpace();
if ((holds != AFF_MAP) || (res != Reals)) {
errh.ErrorExit(sig, "Not an affine map into the Reals", *this);
}
Vector retval = Vector(this->AssocLinear());
return (retval);
}
// ***********************************************************************
// ***********************************************************************
//
// LinearMap Class
//
// ***********************************************************************
// ***********************************************************************
//
// Public member functions
//
// ***********************************************************************
//
// A map can only be cast down to a Linear Map if it is one (no
// conversions).
//
LinearMap::LinearMap(Map &m) : (m)
{
type = LIN_MAP;
if ((holds != LIN_MAP) && (holds != NULL_MAP)) {
errh.ErrorExit("LinearMap::LinearMap(Map &)",
"Attempted to cast a non-linear map to a linear map", m);
}
}
// ***********************************************************************
//
// Need to do memberwise assignment, since Matrix class members need to
// do memberwise assignment:
//
LinearMap& LinearMap::operator=(LinearMap& m)
{
range = m.range;
domain = m.domain;
t = m.t;
invt = m.invt;
invertible = m.invertible;
holds = m.holds;
hasAL = m.hasAL;
isDefault = m.isDefault;
domtype = m.domtype;
rettype = m.rettype;
return (*this);
}
// ***********************************************************************
//
// Output for debugging:
//
void LinearMap::debug_out(ostream &c, int indent)
{
static char* name = "LinearMap Object\n";
this->data_out(c, indent, name);
return;
}
// ***********************************************************************
//
// Simplest kind of map creation. Since we are going between bases,
// the map is invertible, and no subsets are involved:
//
LinearMap::LinearMap(VBasis &b1, VBasis &b2)
{
static char* sig = "LinearMap::LinearMap(VBasis&, VBasis&)";
type = LIN_MAP;
holds = LIN_MAP;
domain = b1.SpaceOf().FullSet();
range = b2.SpaceOf().FullSet();
hasAL = FALSE;
isDefault = FALSE;
domtype = b1.SpaceOf().NativeType();
rettype = b2.SpaceOf().NativeType();
if (range.Dim() != domain.Dim()) {
errh.ErrorExit(sig, "Range and domain do not have same dimension", b1, b2);
}
t = b1.Fromstdb() * b2.Tostdb();
if (fabs(Det(t)) > EPSILON) {
invt = Inverse(t);
invertible = TRUE;
} else {
errh.ErrorExit(sig, "Unexpected error", b1, b2);
}
}
// ***********************************************************************
//
// More general map creation. The domain of the map is a whole space,
// but the range can be a subset. Each item in the GeObList represents
// the image of the corresponding object in the basis.
LinearMap::LinearMap(VBasis& b, VSubSet& s, GeObList& v)
{
static char* sig = "LinearMap::LinearMap(VBasis&, VSubSet&, GeObList&)";
Space rspace = s.EmbeddingSpace();
type = LIN_MAP;
holds = LIN_MAP;
domain = b.SpaceOf().FullSet();
range = s;
hasAL = FALSE;
isDefault = FALSE;
domtype = b.SpaceOf().NativeType();
rettype = rspace.NativeType();
// Make sure that enough image objects have been specified:
if (v.Length() != domain.Dim()) {
errh.ErrorExit(sig, "Incorrect number of objects specified", b, v);
}
// Map the objects into the range subset and use them to start building
// the transform matrix:
GeObType target = rspace.NativeType();
Matrix temp(v.Length(), rspace.Dim());
if (!(this->ConvertList(v, target, range, &temp))) {
errh.ErrorExit(sig, "Object cannot be mapped into range subset", v, range);
}
// Build the transform matrix, and then start to build the inverse by
// converting the range objects to the subspace basis representation:
t = b.Fromstdb() * temp;
temp = temp * range.FromFull();
// Now figure out if the map is invertible. Two requirements must
// be met. The dimension of the range and domain subsets must match,
// and the specified image objects in the range must be independent.
if (range.Dim() != domain.Dim()) {
invertible = FALSE;
} else {
invertible = (fabs(Det(temp)) > EPSILON);
}
if (invertible) {
invt = range.FromFull() * Inverse(temp) * b.Tostdb();
}
}
// ***********************************************************************
//
// The reverse of the above constructor.
//
LinearMap::LinearMap(VSubSet &s, GeObList &v, VBasis &b)
{
static char* sig = "LinearMap::LinearMap(VSubSet&, GeObList&, VBasis&)";
Space dspace = s.EmbeddingSpace();
type = LIN_MAP;
holds = LIN_MAP;
domain = s;
range = b.SpaceOf().FullSet();
hasAL = FALSE;
isDefault = FALSE;
domtype = dspace.NativeType();
rettype = b.SpaceOf().NativeType();
// Make sure that enough preimage objects have been specified:
if (v.Length() != range.Dim()) {
errh.ErrorExit(sig, "Incorrect number of objects specified", b, v, s);
}
if (range.Dim() != domain.Dim()) {
errh.ErrorExit(sig, "Domain and range dimensions do not match", v, s, b);
}
// Map the objects into the domain subset and use them to start building
// the transform matrix:
GeObType target = dspace.NativeType();
Matrix temp1(v.Length(), dspace.Dim());
if (!(this->ConvertList(v, target, domain, &temp1))) {
errh.ErrorExit(sig,
"Object cannot be mapped into domain subset", v, domain);
}
// We now need to confirm that the preimage objects we have been given
// span the domain subset. If they do, build the transform matrix:
Matrix temp2 = temp1 * domain.FromFull();
if (fabs(Det(temp2)) <= EPSILON) {
errh.ErrorExit(sig, "Objects in domain are not independent", v, s);
}
t = domain.FromFull() * Inverse(temp2) * b.Tostdb();
// Maps built this way must be invertible, so build the inverse:
invertible = TRUE;
invt = b.Fromstdb() * temp1;
}
// ***********************************************************************
//
// Here is the most general linear map constructor.
//
LinearMap::LinearMap(VSubSet &s1, GeObList &v1, VSubSet &s2, GeObList &v2)
{
static char* sig =
"LinearMap::LinearMap(VSubSet&, GeObList&, VSubSet&, GeObList&)";
Space dspace = s1.EmbeddingSpace();
Space rspace = s2.EmbeddingSpace();
type = LIN_MAP;
holds = LIN_MAP;
domain = s1;
range = s2;
hasAL = FALSE;
isDefault = FALSE;
domtype = dspace.NativeType();
rettype = rspace.NativeType();
// Make sure that enough preimage and image objects have been specified:
if (v1.Length() != domain.Dim()) {
errh.ErrorExit(sig, "Incorrect number of objects specified", s1, v1);
}
if (v2.Length() != v1.Length()) {
errh.ErrorExit(sig, "Incorrect number of objects specified", s1, s2, v2);
}
// Map the objects into the domain and range subsets and use them to start
// building the transform matrix:
GeObType target = dspace.NativeType();
Matrix tempd(v1.Length(), dspace.Dim());
if (!(this->ConvertList(v1, target, domain, &tempd))) {
errh.ErrorExit(sig,
"Object cannot be mapped into domain subset", v1, domain);
}
target = rspace.NativeType();
Matrix tempr(v2.Length(), rspace.Dim());
if (!(this->ConvertList(v2, target, range, &tempr))) {
errh.ErrorExit(sig,
"Object cannot be mapped into range subset", v2, range);
}
// We now need to confirm that the preimage objects we have been given
// span the domain subset. If they do, build the transform matrix, and
// then start to build the inverse by converting the range objects to
// their subspace basis representation:
Matrix tempd2 = tempd * domain.FromFull();
if (fabs(Det(tempd2)) <= EPSILON) {
errh.ErrorExit(sig, "Objects in domain are not independent", v1, s1);
}
t = domain.FromFull() * Inverse(tempd2) * tempr;
tempr = tempr * range.FromFull();
// Now figure out if the map is invertible. Two requirements must
// be met. The dimension of the range and domain subsets must match,
// and the specified image objects in the range must be independent.
if (range.Dim() != domain.Dim()) {
invertible = FALSE;
} else {
invertible = (fabs(Det(tempr)) > EPSILON);
}
if (invertible) {
invt = range.FromFull() * Inverse(tempr) * tempd;
}
}
// ***********************************************************************
// ***********************************************************************
//
// AffineMap Class
//
// ***********************************************************************
// ***********************************************************************
//
// Private/protected member functions
//
// ***********************************************************************
//
// Builds the default standard affine maps between spaces in a space set.
//
AffineMap::AffineMap(Space &s1, Space &s2)
{
static char* sig = "AffineMap::AffineMap(Space&, Space&)";
// Standard affine maps go between standard affine subsets. Determine
// if these spaces have defined subsets; create them if they do not,
// get them if they do.
domain = s1.StdAffineSubset();
range = s2.StdAffineSubset();
if (domain.Holds() == NULL_SUBSET) {
domain = ASubSet(s1);
}
if (range.Holds() == NULL_SUBSET) {
range = ASubSet(s2);
}
type = AFF_MAP;
holds = AFF_MAP;
invertible = TRUE;
isDefault = TRUE;
domtype = s1.NativeType();
rettype = s2.NativeType();
SRel s1t = s1.ThisSpaceIs();
SRel s2t = s2.ThisSpaceIs();
Space check = s1.GetSpace(s2t);
if (check != s2) {
errh.ErrorExit(sig, "Spaces are not linked", s1, s2);
}
hasAL = (s1t == AFFINE) && (s2t == LINEARIZATION);
t = IdentityMatrix(s1.MatrixSize());
invt = t;
}
// ***********************************************************************
//
// Builds a consistent affine map for linking an affine space to
// a linearization space. USER IS RESPONSIBLE FOR COPYING STD.
// AFFINE SUBSET GENERATED IN THIS ROUTINE TO THE EMBEDDING SPACE.
AffineMap::AffineMap(AffineMap& apm, ProjectiveMap& lpm)
{
Space P = apm.Range().EmbeddingSpace();
Space A = apm.Domain().EmbeddingSpace();
// Linearization space needs a consistent standard affine subset.
range = A.StdAffineSubset();
domain = ASubSet(P.StdAffineSubset(), lpm.Inv());
type = AFF_MAP;
holds = AFF_MAP;
invertible = TRUE;
hasAL = FALSE;
isDefault = FALSE;
domtype = VECTOR;
rettype = AFF_POINT;
t = lpm.t * apm.invt;
invt = Inverse(t);
}
// ***********************************************************************
//
// Builds a consistent affine map for linking an affine space to
// a projective space:
//
AffineMap::AffineMap(ProjectiveMap& lpm, AffineMap& lam)
{
Space L = lpm.Domain().EmbeddingSpace();
Space A = lam.Range().EmbeddingSpace();
// Projective space does not yet have a standard affine subset. It
// must be consistent with the linearization space standard affine
// subset. USER IS RESPONSIBLE FOR COPYING STD.
// AFFINE SUBSET GENERATED IN THIS ROUTINE TO THE EMBEDDING SPACE.
domain = A.StdAffineSubset();
range = ASubSet(L.StdAffineSubset(), lpm);
type = AFF_MAP;
holds = AFF_MAP;
invertible = TRUE;
hasAL = FALSE;
isDefault = FALSE;
domtype = AFF_POINT;
rettype = PROJ_POINT;
t = lam.invt * lpm.t;
invt = Inverse(t);
}
// ***********************************************************************
//
// Public member functions
//
// ***********************************************************************
//
// A map can only be cast down to an affine map if it is one (no
// conversions).
//
AffineMap::AffineMap(Map &m) : (m)
{
type = AFF_MAP;
if ((holds != AFF_MAP) && (holds != NULL_MAP)) {
errh.ErrorExit("AffineMap::AffineMap(Map&)",
"Attempted to cast a non-affine map to an affine map", m);
}
}
// ***********************************************************************
//
// Need to do memberwise assignment, since Matrix class members need to
// do memberwise assignment.
//
AffineMap& AffineMap::operator=(AffineMap& m)
{
range = m.range;
domain = m.domain;
t = m.t;
invt = m.invt;
invertible = m.invertible;
holds = m.holds;
hasAL = m.hasAL;
isDefault = m.isDefault;
domtype = m.domtype;
rettype = m.rettype;
return (*this);
}
// ***********************************************************************
//
// Output for debugging:
//
void AffineMap::debug_out(ostream &c, int indent)
{
static char* name = "AffineMap Object\n";
this->data_out(c, indent, name);
return;
}
// ***********************************************************************
//
// Simplest kind of map creation. Since we are going between bases,
// the map is invertible, and no subsets are involved. Restrict this
// constructor to just handle maps between affine spaces (i.e. no
// maps from simplex to vbasis).
//
AffineMap::AffineMap(Basis &b1, Basis &b2)
{
static char* sig = "AffineMap::AffineMap(Basis&, Basis&)";
type = AFF_MAP;
holds = AFF_MAP;
domain = b1.SpaceOf().FullSet();
range = b2.SpaceOf().FullSet();
hasAL = TRUE;
isDefault = FALSE;
domtype = AFF_POINT;
rettype = AFF_POINT;
// Check that the type of basis matches:
BasisType b1t = b1.Holds();
BasisType b2t = b2.Holds();
if ((b1t != b2t) || !((b1t == SIMPLEX) || (b1t == FRAME))) {
errh.ErrorExit(sig, "Specified bases are not of the correct type", b1, b2);
}
// Check that the dimensionality matches:
if (range.Dim() != domain.Dim()) {
errh.ErrorExit(sig, "Range and domain do not have same dimension", b1, b2);
}
t = b1.Fromstdb() * b2.Tostdb();
if (fabs(Det(t)) > EPSILON) {
invt = Inverse(t);
invertible = TRUE;
} else {
errh.ErrorExit(sig, "Unexpected error", b1, b2);
}
}
// ***********************************************************************
//
// Map from a full affine space into some affine or vector subset.
// We currently require that a simplex be used to define the domain
// preimage.
//
AffineMap::AffineMap(Simplex& b, SubSet& s, GeObList& v)
{
static char* sig = "AffineMap::AffineMap(Simplex&, SubSet&, GeObList&)";
Space rspace = s.EmbeddingSpace();
type = AFF_MAP;
holds = AFF_MAP;
domain = b.SpaceOf().FullSet();
range = s;
domtype = AFF_POINT;
rettype = rspace.NativeType();
hasAL = (rettype != PROJ_POINT);
isDefault = FALSE;
// Make sure the subset is an affine or linear subset and that enough
// image objects have been specified:
if ((s.Holds() != LINEAR_SUBSET) && (s.Holds() != AFFINE_SUBSET)) {
errh.ErrorExit(sig, "Specified subset is not linear or affine", s);
}
if (v.Length() != domain.Dim() + 1) {
errh.ErrorExit(sig, "Incorrect number of objects specified", b, v);
}
// Map the objects into the range subset and use them to start building
// the transform matrix:
GeObType target = rspace.NativeType();
Matrix temp(v.Length(), rspace.MatrixSize());
if (!(this->ConvertList(v, target, range, &temp))) {
errh.ErrorExit(sig, "Object cannot be mapped into range subset", v, range);
}
// Build the transform matrix, and then start to build the inverse by
// converting the range objects to the subspace basis representation:
t = b.Fromstdb() * temp;
temp = temp * range.FromFull();
// Now figure out if the map is invertible. Three requirements must
// be met. The dimension of the range and domain subsets must match,
// the range must be an affine subset, and the specified image objects
// in the range must be independent.
if ((range.Dim() != domain.Dim()) || (range.Holds() != AFFINE_SUBSET)) {
invertible = FALSE;
} else {
invertible = (fabs(Det(temp)) > EPSILON);
}
if (invertible) {
invt = range.FromFull() * Inverse(temp) * b.Tostdb();
}
}
// ***********************************************************************
//
// Map from an affine subset to a full affine space.
//
AffineMap::AffineMap(ASubSet& s, GeObList& v, Simplex& b)
{
static char* sig = "AffineMap::AffineMap(ASubSet&, GeObList&, Simplex&)";
Space dspace = s.EmbeddingSpace();
type = AFF_MAP;
holds = AFF_MAP;
domain = s;
range = b.SpaceOf().FullSet();
domtype = dspace.NativeType();
rettype = AFF_POINT;
hasAL = (domtype == AFF_POINT);
isDefault = FALSE;
// Make sure the subset is an affine subset and that enough
// preimage objects have been specified:
if (v.Length() != range.Dim() + 1) {
errh.ErrorExit(sig, "Incorrect number of objects specified", b, v, s);
}
if (range.Dim() != domain.Dim()) {
errh.ErrorExit(sig, "Domain and range dimensions do not match", v, s, b);
}
// Map the objects into the domain subset and use them to start building
// the transform matrix:
GeObType target = dspace.NativeType();
Matrix temp1(v.Length(), dspace.MatrixSize());
if (!(this->ConvertList(v, target, domain, &temp1))) {
errh.ErrorExit(sig,
"Object cannot be mapped into domain subset", v, domain);
}
// We now need to confirm that the preimage objects we have been given
// span the domain subset. If they do, build the transform matrix:
Matrix temp2 = temp1 * domain.FromFull();
if (fabs(Det(temp2)) <= EPSILON) {
errh.ErrorExit(sig, "Objects in domain are not independent", v, s);
}
t = domain.FromFull() * Inverse(temp2) * b.Tostdb();
// Maps built this way must be invertible, so build the inverse:
invertible = TRUE;
invt = b.Fromstdb() * temp1;
}
// ***********************************************************************
//
// Most general constructor for affine maps.
//
AffineMap::AffineMap(ASubSet &s1, GeObList &v1, SubSet &s2, GeObList &v2)
{
static char* sig =
"AffineMap::AffineMap(ASubSet&, GeObList&, SubSet&, GeObList&)";
Space dspace = s1.EmbeddingSpace();
Space rspace = s2.EmbeddingSpace();
type = AFF_MAP;
holds = AFF_MAP;
domain = s1;
range = s2;
domtype = dspace.NativeType();
rettype = rspace.NativeType();
hasAL = ((domtype == AFF_POINT) && (rettype != PROJ_POINT));
isDefault = FALSE;
// The range subspace can be either affine or linear. Also make
// sure enough objects have been specified in each list:
if ((s2.Holds() != LINEAR_SUBSET) && (s2.Holds() != AFFINE_SUBSET)) {
errh.ErrorExit(sig, "Range subset is not linear or affine", s2);
}
if (v1.Length() != domain.Dim() + 1) {
errh.ErrorExit(sig, "Incorrect number of objects specified", s1, v1);
}
if (v2.Length() != v1.Length()) {
errh.ErrorExit(sig, "Incorrect number of objects specified", v1, s2, v2);
}
// Map the objects into the domain and range subsets and use them to start
// building the transform matrix:
GeObType target = dspace.NativeType();
Matrix tempd(v1.Length(), dspace.MatrixSize());
if (!(this->ConvertList(v1, target, domain, &tempd))) {
errh.ErrorExit(sig,
"Object cannot be mapped into domain subset", v1, domain);
}
target = rspace.NativeType();
Matrix tempr(v2.Length(), rspace.MatrixSize());
if (!(this->ConvertList(v2, target, range, &tempr))) {
errh.ErrorExit(sig,
"Object cannot be mapped into range subset", v2, range);
}
// We now need to confirm that the preimage objects we have been given
// span the domain subset. If they do, build the transform matrix, and
// then start to build the inverse by converting the range objects to
// their subspace basis representation:
Matrix tempd2 = tempd * domain.FromFull();
if (fabs(Det(tempd2)) <= EPSILON) {
errh.ErrorExit(sig, "Objects in domain are not independent", v1, s1);
}
t = domain.FromFull() * Inverse(tempd2) * tempr;
tempr = tempr * range.FromFull();
// Now figure out if the map is invertible. Two requirements must
// be met. The dimension of the range and domain subsets must match,
// and the specified image objects in the range must be independent.
if (range.Dim() != domain.Dim()) {
invertible = FALSE;
} else {
invertible = (fabs(Det(tempr)) > EPSILON);
}
if (invertible) {
invt = range.FromFull() * Inverse(tempr) * tempd;
}
}
// ***********************************************************************
// ***********************************************************************
//
// ProjectiveMap Class
//
// ***********************************************************************
// ***********************************************************************
//
// Private/protected member functions
//
// ***********************************************************************
//
// A core problem when constructing projective maps is scaling the
// rows of the transformation matrix so the "extra" point maps
// to its image. This routine scales the rows of a matrix so the
// specified "unit point" matrix is the sum of the rows. It does this
// by solving a system of equations:
//
Boolean ProjectiveMap::ScaleRows(Matrix* mat, Matrix& unit_pt)
{
if (fabs(Det(*mat)) < EPSILON) {
return (FALSE);
}
Matrix invmat = Inverse(*mat);
// Solve for the row coefficients. If any coefficient = 0, the
// unit point was not in general position. Otherwise, multiply
// the matrix rows by the coefficients, and report the success:
Matrix coeff = unit_pt * invmat;
for (int i = 0; i < coeff.Columns(); i++) {
if (fabs(coeff[0][i]) < EPSILON) {
return (FALSE);
} else {
(*mat)[i] = (coeff[0][i] * (*mat)[i])[0];
}
}
return (TRUE);
}
// ***********************************************************************
//
// Similar to the general ConvertList routine, but sticks the last
// object into a separate Matrix:
//
Boolean ProjectiveMap::ConvertListUnit(GeObList& v, GeObType trg, SubSet& d,
Matrix* temp, Matrix* unit)
{
Space destspace = d.EmbeddingSpace();
for (int i = 0; i < v.Length(); i++) {
GeOb hold = v[i].MapTo(trg);
if (hold.SpaceOf() != destspace) {
return (FALSE);
}
if (!d.IsIn(hold)) {
return (FALSE);
}
if (i == v.Length() - 1) {
(*unit)[0] = hold.MatrixRep()[0];
} else {
(*temp)[i] = hold.MatrixRep()[0];
}
}
return (TRUE);
}
// ***********************************************************************
//
// Builds the default standard projective maps between spaces in a space set.
// One space must be projective, the other a linearization space.
ProjectiveMap::ProjectiveMap(Space& s1, Space& s2)
{
static char* sig = "ProjectiveMap::ProjectiveMap(Space&, Space&)";
domain = s1.FullProjSet();
range = s2.FullProjSet();
type = PROJ_MAP;
holds = PROJ_MAP;
invertible = TRUE;
hasAL = FALSE;
isDefault = TRUE;
domtype = domain.Accepts();
rettype = range.Accepts();
SRel s1t = s1.ThisSpaceIs();
SRel s2t = s2.ThisSpaceIs();
Space check = s1.GetSpace(s2t);
if (check != s2) {
errh.ErrorExit(sig, "Spaces are not linked", s1, s2);
}
t = IdentityMatrix(s1.MatrixSize());
invt = t;
}
// ***********************************************************************
//
// Builds a consistent projective map for linking a projective space to
// a linearization space.
//
ProjectiveMap::ProjectiveMap(VSpace& L, AffineMap& lpm, PSpace& P)
{
domain = L.FullProjSet();
range = P.FullSet();
type = PROJ_MAP;
holds = PROJ_MAP;
invertible = TRUE;
hasAL = FALSE;
isDefault = lpm.isDefault;
domtype = VEC_EC;
rettype = PROJ_POINT;
t = lpm.t;
invt = Inverse(t);
}
// ***********************************************************************
//
// Public member functions
//
// ***********************************************************************
//
// A map can only be cast down to a Projective Map if it is one (no
// conversions).
ProjectiveMap::ProjectiveMap(Map &m) : (m)
{
type = PROJ_MAP;
if ((holds != PROJ_MAP) && (holds != NULL_MAP)) {
errh.ErrorExit("ProjectiveMap::ProjectiveMap(Map&)",
"Attempted to cast a non-projective map to a projective map", m);
}
}
// ***********************************************************************
//
// Need to do memberwise assignment, since Matrix class members need to
// do memberwise assignment:
//
ProjectiveMap& ProjectiveMap::operator=(ProjectiveMap& m)
{
range = m.range;
domain = m.domain;
t = m.t;
invt = m.invt;
invertible = m.invertible;
holds = m.holds;
hasAL = m.hasAL;
isDefault = m.isDefault;
domtype = m.domtype;
rettype = m.rettype;
return (*this);
}
// ***********************************************************************
//
// Debug output
//
void ProjectiveMap::debug_out(ostream &c, int indent)
{
static char* name = "ProjectiveMap Object\n";
this->data_out(c, indent, name);
return;
}
// ***********************************************************************
//
// Used for invertible maps between whole projective spaces:
//
ProjectiveMap::ProjectiveMap(HFrame &b1, HFrame &b2)
{
static char* sig = "ProjectiveMap::ProjectiveMap(HFrame&, HFrame&)";
type = PROJ_MAP;
holds = PROJ_MAP;
domain = b1.SpaceOf().FullSet();
range = b2.SpaceOf().FullSet();
hasAL = FALSE;
isDefault = FALSE;
domtype = PROJ_POINT;
rettype = PROJ_POINT;
// Check that the type of basis matches:
if ((b1.Holds() != HFRAME) || (b2.Holds() != HFRAME)) {
errh.ErrorExit(sig, "Specified bases are not projective frames", b1, b2);
}
// Check that the dimensionality matches:
if (range.Dim() != domain.Dim()) {
errh.ErrorExit(sig, "Range and domain do not have same dimension", b1, b2);
}
t = b1.Fromstdb() * b2.Tostdb();
if (fabs(Det(t)) > EPSILON) {
invt = Inverse(t);
invertible = TRUE;
} else {
errh.ErrorExit(sig, "Unexpected error", b1, b2);
}
}
// ***********************************************************************
//
// Used for invertible maps from a whole projective space to
// a projective subset. Since the only allowable method for
// creating non-invertible maps is to have a domain subset with
// removed points, we require the image objects to be independent.
// Note that the subset could be from a vector space. The range
// subset cannot have removed points, since there would be no way
// of returning a unique point in the embedding space.
//
ProjectiveMap::ProjectiveMap(HFrame &b, PSubSet &s, GeObList &v)
{
static char* sig =
"ProjectiveMap::ProjectiveMap(HFrame&, PSubSet&, GeObList&)";
Space rspace = s.EmbeddingSpace();
type = PROJ_MAP;
holds = PROJ_MAP;
domain = b.SpaceOf().FullSet();
range = s;
hasAL = FALSE;
isDefault = FALSE;
domtype = PROJ_POINT;
rettype = s.Accepts();
// Make that enough image objects have been specified. Also, since
// only invertible maps can be specified, check the dimensions:
if (v.Length() != domain.Dim() + 2) {
errh.ErrorExit(sig, "Incorrect number of objects specified", b, v);
}
if (range.Dim() != domain.Dim()) {
errh.ErrorExit(sig, "Range and domain dimensions must be equal",
range, domain);
}
if (range.HasRemovedPoints()) {
errh.ErrorExit(sig, "Range subset cannot have removed points", range);
}
// Map the objects into the range subset and use them to start building
// the transform matrix:
int rdim = rspace.MatrixSize();
Matrix temp(v.Length() - 1, rdim);
Matrix unit(1, rdim);
if (!(this->ConvertListUnit(v, rettype, range, &temp, &unit))) {
errh.ErrorExit(sig, "Object cannot be mapped into range subset", v, range);
}
// Convert the range objects to the range subset basis representation, then
// scale the matrix rows:
temp = temp * range.FromFull();
unit = unit * range.FromFull();
if (!(this->ScaleRows(&temp, unit))) {
errh.ErrorExit(sig, "Range objects not in general position", v, s);
}
// Build the transform matrix, then build the inverse:
t = b.Fromstdb() * temp * range.ToFull();
invt = range.FromFull() * Inverse(temp) * b.Tostdb();
invertible = TRUE;
}
// ***********************************************************************
//
// Used for map from a projective subset to a projective space. If
// subset has removed points, map will be non-invertible:
//
ProjectiveMap::ProjectiveMap(PSubSet &s, GeObList &v, HFrame &b)
{
static char* sig =
"ProjectiveMap::ProjectiveMap(PSubSet&, GeObList&, HFrame&)";
Space dspace = s.EmbeddingSpace();
type = PROJ_MAP;
holds = PROJ_MAP;
domain = s;
range = b.SpaceOf().FullSet();
hasAL = FALSE;
isDefault = FALSE;
domtype = s.Accepts();
rettype = PROJ_POINT;
// Make sure that enough preimage objects have been specified.
// Check that the domain and range dimensions match.
if (v.Length() != range.Dim() + 2) {
errh.ErrorExit(sig, "Incorrect number of objects specified", b, v, s);
}
if (range.Dim() != domain.Dim()) {
errh.ErrorExit(sig, "Domain and range dimensions do not match", v, s, b);
}
// Map the objects into the domain subset and use them to start building
// the transform matrix:
int ddim = dspace.MatrixSize();
Matrix temp(v.Length() - 1, ddim);
Matrix unit(1, ddim);
if (!(this->ConvertListUnit(v, domtype, domain, &temp, &unit))) {
errh.ErrorExit(sig,
"Object cannot be mapped into domain subset", v, domain);
}
// Convert the domain objects to the domain subset basis representation, then
// scale the matrix rows:
temp = temp * domain.FromFull();
unit = unit * domain.FromFull();
if (!(this->ScaleRows(&temp, unit))) {
errh.ErrorExit(sig, "Domain objects not in general position", v, s);
}
// Build the transform matrix:
t = domain.FromFull() * Inverse(temp) * b.Tostdb();
// The invertibility of this transform depends on whether the domain
// subset has been defined to have removed points. If it has, the
// map is not invertible. Otherwise, calculate the inverse:
invertible = !domain.HasRemovedPoints();
if (invertible) {
invt = b.Fromstdb() * temp * domain.ToFull();
}
}
// ***********************************************************************
//
// Used for maps between subsets. If domain subset has removed points,
// map will not be invertible:
//
ProjectiveMap::ProjectiveMap(PSubSet &s1, GeObList &v1,
PSubSet &s2, GeObList &v2)
{
static char* sig =
"ProjectiveMap::ProjectiveMap(PSubSet&, GeObList&, PSubSet&, GeObList&)";
Space dspace = s1.EmbeddingSpace();
Space rspace = s2.EmbeddingSpace();
type = PROJ_MAP;
holds = PROJ_MAP;
domain = s1;
range = s2;
hasAL = FALSE;
isDefault = FALSE;
domtype = s1.Accepts();
rettype = s2.Accepts();
// Make sure that enough preimage and image objects have been specified.
// Check that the domain and range dimensions match.
if (v1.Length() != domain.Dim() + 2) {
errh.ErrorExit(sig, "Incorrect number of objects specified", v1, s1);
}
if (v2.Length() != range.Dim() + 2) {
errh.ErrorExit(sig, "Incorrect number of objects specified", v2, s2);
}
if (range.Dim() != domain.Dim()) {
errh.ErrorExit(sig, "Domain and range dimensions do not match", s1, s2);
}
if (range.HasRemovedPoints()) {
errh.ErrorExit(sig, "Range subset cannot have removed points", range);
}
// Map the objects into the domain and range subsets and use them to start
// building the transform matrix:
int ddim = dspace.MatrixSize();
Matrix tempd(v1.Length() - 1, ddim);
Matrix unitd(1, ddim);
if (!(this->ConvertListUnit(v1, domtype, domain, &tempd, &unitd))) {
errh.ErrorExit(sig,
"Object cannot be mapped into domain subset", v1, domain);
}
int rdim = rspace.MatrixSize();
Matrix tempr(v2.Length() - 1, rdim);
Matrix unitr(1, rdim);
if (!(this->ConvertListUnit(v2, rettype, range, &tempr, &unitr))) {
errh.ErrorExit(sig,
"Object cannot be mapped into range subset", v2, range);
}
// Convert the domain objects to the domain subset basis representation, then
// scale the matrix rows. Do the same for the range:
tempd = tempd * domain.FromFull();
unitd = unitd * domain.FromFull();
if (!(this->ScaleRows(&tempd, unitd))) {
errh.ErrorExit(sig, "Domain objects not in general position", v1, s1);
}
tempr = tempr * range.FromFull();
unitr = unitr * range.FromFull();
if (!(this->ScaleRows(&tempr, unitr))) {
errh.ErrorExit(sig, "Range objects not in general position", v2, s2);
}
// Build the transform matrix:
t = domain.FromFull() * Inverse(tempd) * tempr * range.ToFull();
// The invertibility of this transform depends on whether the domain
// subset has been defined to have removed points. If it has, the
// map is not invertible. Otherwise, calculate the inverse:
invertible = !domain.HasRemovedPoints();
if (invertible) {
invt = range.FromFull() * Inverse(tempr) * tempd * domain.ToFull();
}
}
// ***********************************************************************